package com.dap.configuration.service.config.service.impl;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.baidu.disconf.core.common.constants.DisConfigTypeEnum;
import com.baidu.disconf.web.config.ApplicationPropertyConfig;
import com.baidu.disconf.web.service.app.bo.App;
import com.baidu.disconf.web.service.app.service.AppMgr;
import com.baidu.disconf.web.service.config.bo.Config;
import com.baidu.disconf.web.service.config.bo.ConfigHistory;
import com.baidu.disconf.web.service.config.dao.ConfigDao;
import com.baidu.disconf.web.service.config.form.ConfNewForm;
import com.baidu.disconf.web.service.config.form.ConfNewItemForm;
import com.baidu.disconf.web.service.env.bo.Env;
import com.baidu.disconf.web.service.env.service.EnvMgr;
import com.baidu.disconf.web.service.user.service.AuthMgr;
import com.baidu.disconf.web.utils.CodeUtils;
import com.baidu.disconf.web.utils.DiffUtils;
import com.baidu.disconf.web.web.config.validator.ConfigValidator;
import com.baidu.dsp.common.constant.DataFormatConstants;
import com.baidu.dsp.common.exception.FieldException;
import com.baidu.dsp.common.utils.DataTransfer;
import com.baidu.dsp.common.utils.ServiceUtil;
import com.baidu.dsp.common.utils.email.LogMailBean;
import com.baidu.ub.common.db.DaoPageResult;
import com.dap.configuration.service.config.bo.ConfigParam;
import com.dap.configuration.service.config.dao.ConfigExtensionDao;
import com.dap.configuration.service.config.dao.HistoryDao;
import com.dap.configuration.service.config.form.ConfListExtensionForm;
import com.dap.configuration.service.config.form.ConfNewExtensionForm;
import com.dap.configuration.service.config.service.ConfigExtensionMgr;
import com.dap.configuration.service.config.vo.ConfListExtensionVo;
import com.dap.configuration.web.zookeeper.ZooKeeperExtensionDriver;
import com.github.knightliao.apollo.utils.data.GsonUtils;
import com.github.knightliao.apollo.utils.io.OsUtil;
import com.github.knightliao.apollo.utils.time.DateUtils;

/**
 * @author dengxiang
 * @version 2014-6-16
 */
@Service
public class ConfigMgrExtensionImpl implements ConfigExtensionMgr {

	private static final Logger LOG = LoggerFactory
			.getLogger(ConfigMgrExtensionImpl.class);

	@Autowired
	private ConfigExtensionDao configExtensionDao;
	
	@Autowired
	private HistoryDao historyDao;

	@Autowired
	private ConfigDao configDao;

	@Autowired
	private ConfigValidator configValidator;

	@Autowired
	private AppMgr appMgr;

	@Autowired
	private EnvMgr envMgr;

	@Autowired
	private ZooKeeperExtensionDriver zooKeeperExtensionDriver;

	@Autowired
	private LogMailBean logMailBean;

	@Autowired
	private ApplicationPropertyConfig applicationPropertyConfig;

	@Autowired
	private AuthMgr authMgr;
	
	@Override
	public DaoPageResult<ConfListExtensionVo> getConfigList(
			ConfListExtensionForm confListForm, boolean fetchZk,
			final boolean getErrorMessage) {

		// 数据据结果
		if(confListForm.getBankKey() == null || confListForm.getBankKey()==""){
			throw new FieldException("config", "bankKey不能为空", null);
		}
		DaoPageResult<Config> configList = configExtensionDao.getConfigList(
				confListForm.getAppId(), confListForm.getEnvId(),
				confListForm.getVersion(), confListForm.getBankKey(),
				confListForm.getStartTime(),confListForm.getEndTime(),
				confListForm.getUserId(),confListForm.getName(), confListForm.getPage());
		//获取应用、环境值用于查询zk上数据
		final App app = appMgr.getById(confListForm.getAppId());
		final Env env = envMgr.getById(confListForm.getEnvId());
		// 进行转换
		DaoPageResult<ConfListExtensionVo> configListVo = ServiceUtil
				.getResult(configList,
						new DataTransfer<Config, ConfListExtensionVo>() {

							@Override
							public ConfListExtensionVo transfer(Config input) {
								String appNameString = app.getName();
								String envName = env.getName();
								ConfListExtensionVo configListVo = convert(
										input, appNameString, envName);

								return configListVo;
							}
						});

		return configListVo;
	}

	/**
	 * 根据 配置ID获取配置返回
	 */
	@Override
	public ConfListExtensionVo getConfVo(Long configId) {
		Config config = configDao.get(configId);
		App app = appMgr.getById(config.getAppId());
		Env env = envMgr.getById(config.getEnvId());
		return convert(config, app.getName(), env.getName());
	}

	/**
	 * 校验新建 配置
	 *
	 * @param confNewForm
	 * @param disConfigTypeEnum
	 */
	public void validateNew(ConfNewExtensionForm confNewForm,
			DisConfigTypeEnum disConfigTypeEnum) {
		// app
		if(StringUtils.isEmpty(confNewForm.getKey())){
			throw new FieldException("config", "key值不能为空", null);
		}
		if(StringUtils.isEmpty(confNewForm.getAppId().toString())){
			throw new FieldException("config", "app不能为空", null);
		}
		if(StringUtils.isEmpty(confNewForm.getEnvId().toString())){
			throw new FieldException("config", "env不能为空", null);
		}
		if(StringUtils.isEmpty(confNewForm.getVersion())){
			throw new FieldException("config", "版本不能为空", null);
		}
		if (confNewForm.getBankKey() == null || confNewForm.getBankKey() == "") {
			throw new FieldException("config", "bankKey不存在", null);
		}
		App app = appMgr.getById(confNewForm.getAppId());
		if (app == null) {
			throw new FieldException("config", "appId无效", null);
		}
		boolean ret = authMgr.verifyApp4CurrentUser(app.getId());
		if (ret == false) {
			throw new FieldException("config", "app权限不存在",
					null);
		}
		Env env = envMgr.getById(confNewForm.getEnvId());
		if (env == null) {
			throw new FieldException("config", "envId无效", null);
		}
		Config config = getConfByParameter(app.getId(), env.getId(),
				confNewForm.getVersion(), confNewForm.getKey(),
				disConfigTypeEnum, confNewForm.getBankKey(),
				confNewForm.getUserId());
		if (config != null) {
			throw new FieldException("config", "录入配置信息已存在", null);
		}
	}

	/**
	 * 新建配置
	 */
	@Override
	public Config newConfig(ConfNewExtensionForm confNewForm,
			DisConfigTypeEnum disConfigTypeEnum) {
		Config config = new Config();
		config.setAppId(confNewForm.getAppId());
		config.setEnvId(confNewForm.getEnvId());
		config.setName(confNewForm.getKey());
		config.setType(disConfigTypeEnum.getType());
		config.setVersion(confNewForm.getVersion());
		config.setValue(confNewForm.getValue());
		config.setBankKey(confNewForm.getBankKey());
		config.setUserId(confNewForm.getUserId());
		String curTime = DateUtils.format(new Date(),DataFormatConstants.COMMON_TIME_FORMAT);
		config.setCreateTime(curTime);
		config.setUpdateTime(curTime);
		Config config1 = configDao.create(config);
		ConfigHistory history = new ConfigHistory();
		try {
			BeanUtils.copyProperties(history, config1);
		} catch (IllegalAccessException | InvocationTargetException e) {
			throw new FieldException("config", "历史记录赋值出错", null);
		}
	
		history.setConfigId(config1.getId());
		history.setId(null);
		history.setAction("insert");
		historyDao.create(history);
		// 发送邮件通知
		String toEmails = appMgr.getEmails(config.getAppId());
		if (applicationPropertyConfig.isEmailMonitorOn() == true) {
			logMailBean.sendHtmlEmail(toEmails," config new",getNewValue(confNewForm.getValue(), config.toString(),
							getConfigUrlHtml(config)));
		}
		
		return config1;
		
	}

	/**
	 * 根据详细参数获取配置
	 */
	@Override
	public Config getConfByParameter(Long appId, Long envId, String version,
			String key, DisConfigTypeEnum disConfigTypeEnum, String bankKey,
			String userId) {

		Config config = configExtensionDao.getByParameter(appId, envId,
				version, key, disConfigTypeEnum, bankKey, userId);
		return config;
	}

	/**
	 * 转换成配置返回
	 * @param config
	 * @return
	 */
	private ConfListExtensionVo convert(Config config, String appNameString,
			String envName) {
		ConfListExtensionVo confListVo = new ConfListExtensionVo();
		confListVo.setConfigId(config.getId());
		confListVo.setAppId(config.getAppId());
		confListVo.setAppName(appNameString);
		confListVo.setEnvName(envName);
		confListVo.setEnvId(config.getEnvId());
		confListVo.setCreateTime(config.getCreateTime());
		confListVo.setModifyTime(config.getUpdateTime());
		confListVo.setKey(config.getName());
		if(StringUtils.isEmpty(config.getValue())){
			confListVo.setValue(null);
		}else{
			confListVo.setValue(CodeUtils.unicodeToUtf8(config.getValue()));
		}
		confListVo.setVersion(config.getVersion());
		confListVo.setType(DisConfigTypeEnum.getByType(config.getType())
				.getModelName());
		confListVo.setTypeId(config.getType());
		confListVo.setBankKey(config.getBankKey());
		if (config.getUserId() != null) {
			confListVo.setUserId(config.getUserId());
		}
		return confListVo;
	}


	/**
	 * @return
	 */
	private String getConfigUrlHtml(Config config) {

		return "<br/>点击<a href='http://"
				+ applicationPropertyConfig.getDomain()
				+ "/modifyFile.html?configId=" + config.getId()
				+ "'> 这里 </a> 进入查看<br/>";
	}

	/**
	 * @param newValue
	 * @param identify
	 * @return
	 */
	private String getNewValue(String newValue, String identify,
			String htmlClick) {
		String contentString = StringEscapeUtils.escapeHtml(identify) + "<br/>"
				+ htmlClick + "<br/><br/> ";
		String data = "<br/><br/><br/><span style='color:#FF0000'>New value:</span><br/>";
		contentString = contentString + data
				+ StringEscapeUtils.escapeHtml(newValue);
		return contentString;
	}

	@Override
	public List<File> getDisonfFileList(ConfListExtensionForm confListForm) throws IOException {
		List<Config> configList = configExtensionDao.getConfigList(
				confListForm.getAppId(), confListForm.getEnvId(),
				confListForm.getVersion(), confListForm.getBankKey(),
				confListForm.getUserId());
		// 时间作为当前文件夹
		String curTime = DateUtils.format(new Date(),
				DataFormatConstants.COMMON_TIME_FORMAT);
		curTime = "tmp" + File.separator + curTime;
		OsUtil.makeDirs(curTime);
		App app = appMgr.getById(confListForm.getAppId());
		Env env = envMgr.getById(confListForm.getEnvId());
		ConfigParam param = new ConfigParam();
		param.setAppName(app.getName());
		param.setEnvName(env.getName());
		param.setBankKey(confListForm.getBankKey());
		param.setVersion(confListForm.getVersion());
		String str = JSON.toJSONString(param, true);
		//将参数全部写到参数文件 param.properties -- 参数+文件可以确定唯一的一条config记录
		File paramFile = new File(curTime, "param.properties");//参数文件
		paramFile.setReadOnly();//设置参数文件为只读
		File itemFile = new File(curTime, "config_item_param.properties");//配置项文件
		StringBuffer itemContent = new StringBuffer();
		FileUtils.writeByteArrayToFile(paramFile, str.getBytes());
		List<File> files = new ArrayList<File>();
		for (Config config : configList) {
			if (config.getType().equals(DisConfigTypeEnum.FILE.getType())) {
				//对于配置文件名称中包含"/"的配置，通过名称来创建文件，如果不进行转码，创建后的文件会丢失"/"之前的部分
				File file = new File(curTime, URLEncoder.encode(config.getName(), "UTF-8"));
				if(StringUtils.isEmpty(config.getValue())){
					FileUtils.writeByteArrayToFile(file,"".getBytes());
				}else{
					FileUtils.writeByteArrayToFile(file, config.getValue()
							.getBytes());
				}
				
				files.add(file);
			} else {
				//配置项不需要生成单独文件，所以不需要转码
				itemContent.append(config.getName() + "=" + config.getValue()
						+ "\r\n");
			}
		}
		FileUtils.writeByteArrayToFile(itemFile, itemContent.toString()
					.getBytes());
		files.add(paramFile);
		files.add(itemFile);
		return files;
	}

	@Override
	public Config getListByConfig(Config config) {
		DisConfigTypeEnum disConfigTypeEnum = null;
		if (config.getType() == 0) {
			disConfigTypeEnum = DisConfigTypeEnum.FILE;
		} else {
			disConfigTypeEnum = DisConfigTypeEnum.ITEM;
		}
		Config config1 = configExtensionDao.getByParameter(config.getAppId(),
				config.getEnvId(), config.getVersion(), config.getName(),
				disConfigTypeEnum, config.getBankKey(), config.getUserId());
		return config1;
	}

	/**
	 * 通知Zookeeper, 失败时不回滚数据库,通过监控来解决分布式不一致问题
	 */
	@Override
	public void notifyZookeeper(Long configId) {
		ConfListExtensionVo confListVo = getConfVo(configId);
		if (confListVo.getTypeId().equals(DisConfigTypeEnum.FILE.getType())) {
			zooKeeperExtensionDriver.notifyNodeUpdate(confListVo.getAppName(),
					confListVo.getEnvName(), confListVo.getVersion(),
					confListVo.getKey(),
					GsonUtils.toJson(confListVo.getValue()),
					confListVo.getBankKey(), DisConfigTypeEnum.FILE);
		} else {
			zooKeeperExtensionDriver.notifyNodeUpdate(confListVo.getAppName(),
					confListVo.getEnvName(), confListVo.getVersion(),
					confListVo.getKey(), confListVo.getValue(),
					confListVo.getBankKey(), DisConfigTypeEnum.ITEM);
		}

	}

	@Override
	public void newHistory(Long configId,String value,String action) {
		Config config = configDao.get(configId);
		ConfigHistory history = new ConfigHistory();
		try {
			BeanUtils.copyProperties(history, config);
		} catch (IllegalAccessException e) {
			throw new FieldException("config", "历史记录赋值出错", null);
		} catch (InvocationTargetException e) {
			throw new FieldException("config", "历史记录赋值出错", null);
		}
		if(config.getId() != null){
			history.setConfigId(config.getId());
		}
		if(action != null){
			history.setAction(action);
		}
		if(value != null){
			history.setValue(value);
		}
		history.setId(null);
		historyDao.create(history);
	}

	@Override
	public ConfigParam convertParam(String str) {
		JSON json = JSON.parseObject(str);
		ConfigParam configParam = json.toJavaObject(json, ConfigParam.class);
		App app = appMgr.getByName(configParam.getAppName());
		Env env = envMgr.getByName(configParam.getEnvName());
		configParam.setAppName(app.getId().toString());
		configParam.setEnvName(env.getId().toString());
		return configParam;
	}
	
	/**
     * 更新 配置项/配置文件 的值
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = RuntimeException.class)
    public String updateItemValue(Long configId, String value) {

        Config config = getConfigById(configId);
        String oldValue = null;
        if(!StringUtils.isEmpty(config.getValue())){
        	oldValue = CodeUtils.unicodeToUtf8(config.getValue());
        }
        if(StringUtils.isEmpty(value)){
        	configDao.updateValue(configId, null);
        }else{
        	if(StringUtils.isEmpty(value)){
        		configDao.updateValue(configId, value);
        	}else{
        		configDao.updateValue(configId, CodeUtils.utf8ToUnicode(value));
        	}
        	
        }
        
        // 发送邮件通知
        String toEmails = appMgr.getEmails(config.getAppId());

        if (applicationPropertyConfig.isEmailMonitorOn() == true) {
            boolean isSendSuccess = logMailBean.sendHtmlEmail(toEmails,
                    " config update", DiffUtils.getDiff(CodeUtils.unicodeToUtf8(oldValue),
                            value,
                            config.toString(),
                            getConfigUrlHtml(config)));
            if (isSendSuccess) {
                return "修改成功，邮件通知成功";
            } else {
                return "修改成功，邮件发送失败，请检查邮箱配置";
            }
        }

        return "修改成功";
    }

    /**
     * 根据配置ID获取配置
     */
    @Override
    public Config getConfigById(Long configId) {

        return configDao.get(configId);
    }
}
